/**
 * 20220304:[require] to [import]...jordia
 */
import {Scene,OrthographicCamera,ShaderMaterial,PlaneGeometry,Mesh} from 'three';

/**
 * 渲染切换淡入淡出（主要用于场景切换）
 * @param renderer {renderer} 用于显示淡入淡出的渲染器
 * @param size {object} 渲染区尺寸{left,top,width,height}
 * @param values {object} 参数，如下：
 * texture：用于淡入淡出样式的贴图（按灰度），
 * threshold：样式贴图的值计算阈值，
 * mixRatio：混合比值。
 */
export default class RenderTransition{
    constructor(renderer,size,values={}){
        this._renderer=renderer;
        this._size=size;
        this._values=values;
        this.init();
    }

    //--------------------------------------对外属性及方法--------------------------------------

    /**
     * 设置渲染区尺寸（窗体尺寸改变时必须调用）
     * @param size {object} {width，height}
     */
    set size(size){
        this._size=size;
        this._camera.left= -this._size.width/2;
        this._camera.right=this._size.width/2;
        this._camera.top=this._size.height/2;
        this._camera.bottom=-this._size.height/2;
        this._camera.updateProjectionMatrix();
        this._quad.scale.set(this._size.width,this._size.height,1);
    }

    /**
     * 设置样式贴图（按灰度，不设置将使用默认淡入淡出样式）
     * @param tex
     */
    set texture(tex){
        let needsUpdate=(this._texture===null&&tex!==null)||(this._texture!==null&&tex===null);
        this._texture=tex;
        this._quadMaterial.uniforms.tMixTexture.value=tex;
        if(needsUpdate){
            this._quadMaterial.fragmentShader=this.fragmentShader;
            this._quadMaterial.needsUpdate=true;
        }
    }

    /**
     * 设置贴图计算阈值
     * @param num
     */
    set threshold(num){
        this._quadMaterial.uniforms.threshold.value=num;
    }

    /**
     * 设置当前的渲染信息
     * @param renderTarget {WebGLRenderTarget} 当前渲染对象
     * @param renderFunc {function} 不进行切换时的渲染方法
     * @param rttRenderFunc {function} 切换时的渲染方法（必须写入到renderTarget）
     */
    setRenderFrom(renderTarget,renderFunc,rttRenderFunc){
        this._quadMaterial.uniforms.tDiffuse1.value=renderTarget.texture;
        this._targetFrom={
            renderTarget:renderTarget,
            render:renderFunc,
            rttRender:rttRenderFunc
        };
    }

    /**
     * 设置目标的渲染信息
     * @param renderTarget {WebGLRenderTarget} 目标渲染对象
     * @param renderFunc {function} 不进行切换时的渲染方法
     * @param rttRenderFunc {function} 切换时的渲染方法（必须写入到renderTarget）
     */
    setRenderTo(renderTarget,renderFunc,rttRenderFunc){
        this._quadMaterial.uniforms.tDiffuse2.value=renderTarget.texture;
        this._targetTo={
            renderTarget:renderTarget,
            render:renderFunc,
            rttRender:rttRenderFunc
        };
    }

    /**
     * 切换渲染的进度
     * @param prg {number} 0：不切换，1：切换完成
     */
    translate(prg) {
        this._prg = prg > 1 ? 1 : (prg < 0 ? 0 : prg);
        this._quadMaterial.uniforms.mixRatio.value = this._prg;
        if(this._prg===0){
            this._targetFrom.render();
        }else if(this._prg===1){
            this._targetTo.render();
        }else{
            this._targetFrom.rttRender();
            this._targetTo.rttRender();
            this._renderer.setRenderTarget(null);
            this._renderer.clear();
            this._renderer.render(this._scene,this._camera);
        }
    }

    //---------------------------------------私有属性及方法--------------------------------------

    init(){
        this.initVariable();
        this.initComponents();
    }
    initVariable(){
        this._scene=null;
        this._camera=null;
        this._quadMaterial=null;
        this._quad=null;
        this._targetFrom=null;
        this._targetTo=null;
        this._texture=this._values.texture||null;
    }
    initComponents(){
        this.initScene();
        this.initCamera();
        this.initQuad();
    }
    initScene(){
        this._scene=new Scene();
    }
    initCamera(){
        this._camera=new OrthographicCamera(
            -this._size.width/2,
            this._size.width/2,
            this._size.height/2,
            -this._size.height/2,
            -10,10);
        this._camera.updateProjectionMatrix();
    }
    getQuadMaterial(){
        let mat=new ShaderMaterial();
        mat.uniforms={
            tDiffuse1: {value: null},
            tDiffuse2: {value: null},
            mixRatio: {value: this._values.mixRatio!==undefined?this._values.mixRatio:0.0},
            threshold: {value: this._values.threshold||0.01},
            tMixTexture: {value: this._texture}
        };
        mat.vertexShader=this.vertexShader;
        mat.fragmentShader=this.fragmentShader;
        return mat;
    }
    initQuad(){
        this._quadMaterial=this.getQuadMaterial();
        let geo=new PlaneGeometry(1,1);
        this._quad=new Mesh(geo,this._quadMaterial);
        this._quad.scale.set(this._size.width,this._size.height,1);
        this._scene.add(this._quad);
    }
    get vertexShader(){
        return [
            "varying vec2 vUv;",
            "void main() {",
            "   vUv = vec2( uv.x, uv.y );",
            "   gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
            "}"
        ].join( "\n" );
    }
    get fragmentShader(){
        return [
            this._texture?"#define USE_TEXTURE":"",
            "uniform float mixRatio;",
            "uniform sampler2D tDiffuse1;",
            "uniform sampler2D tDiffuse2;",
            "#ifdef USE_TEXTURE",
            "   uniform sampler2D tMixTexture;",
            "#endif",
            "uniform float threshold;",
            "varying vec2 vUv;",
            "void main() {",
            "	vec4 texel1 = texture2D( tDiffuse1, vUv );",
            "	vec4 texel2 = texture2D( tDiffuse2, vUv );",
            "	#ifdef USE_TEXTURE",
            "		vec4 transitionTexel = texture2D( tMixTexture, vUv );",
            "		float r = mixRatio * (1.0 + threshold * 2.0) - threshold;",
            "		float mixf=clamp((transitionTexel.r - r)*(1.0/threshold), 0.0, 1.0);",
            "		gl_FragColor = mix( texel2, texel1, mixf );",
            "	#else",
            "		gl_FragColor = mix( texel1, texel2, mixRatio );",
            "	#endif",
            "}"
        ].join( "\n" );
    }
}